iT邦幫忙

2026 iThome 鐵人賽

DAY 20
1
ChatGPT & Codex

利用Custom GPT+遊戲感來寫PRD系列 第 20

【Day 20】區塊 7 Edge Cases——上線後出事的,多半是沒人想過的那些「萬一」

  • 分享至 

  • xImage
  •  

有一次我們上線一個會員權益兌換的功能,做的是連鎖咖啡品牌的點數換贈品。Demo 的時候流程完全順利:業務照著點下去,兌換成功,畫面跳出「兌換完成」。

Day 20

上線第三天,工程師在群組丟了一句:「有人點兩下,扣了兩次點數。」

需求討論時,大家想的都是使用者照著流程走會發生什麼;實際會出事的,是使用者沒照著走的時候。網路斷一下、重複點兩下、點數差三點、後台主管請假沒簽核。這些「萬一」在需求階段沒人提,上線後會一個一個出現,每一個都是一張 bug 單。

區塊 7 The Edge Cases 處理的就是這一段。它不問「功能要做什麼」,只負責把「萬一怎麼辦」在需求階段先攤開。

例外情境是規格的反面

先講一個前提:例外情境其實是規格基線的逆否命題。

Day 16 講規格基線時,我們會問「單檔上限多少」「兌換點數下限是多少」「一次最多選幾項」。這些是正向的 spec。而例外情境就是這些 spec 的反面:規格說「上限 10MB」,例外就問「超過 10MB 怎麼處理」;規格說「點數要滿 500 才能換」,例外就問「點數不夠時畫面怎麼回」。

所以鼠勾以有個硬性順序:先過規格基線,再進例外情境。如果某個規格還沒確認,對應的例外情境它不會硬寫,而是標一個 [待確認],備註「待規格基線的單檔上限確認後才能寫驗收條件」。

為什麼要這樣綁?因為例外情境的驗收條件需要具體數字。你沒辦法寫「超過上限時提示」這種 AC,那等於沒寫。你得寫「超過 10MB 時,顯示『檔案過大,請壓縮後再上傳』」。沒有那個 10MB,這條 AC 就是空的。先把正面講定,反面才寫得出來。

十類功能類型,動態撈出該想的例外

例外情境的麻煩在於太發散。一個表單會出錯的地方,跟一個付款流程會出錯的地方,內容差很多。如果只丟一句「請列出所有可能的例外」給使用者,他大概能寫出三條,上線後會冒出十條。

鼠勾以的做法是先判功能類型,再帶出對應的清單。它把功能分成十類:

類型 看到什麼關鍵字會判進來
表單類 輸入、填寫、送出、申請
選擇類 選擇、挑選、勾選、點選
查詢類 查詢、搜尋、列表、顯示
交易類 付款、扣款、兌換、繳費
預約類 預約、排程、時段、名額
上傳類 上傳、附件、檔案、圖片
通知類 通知、提醒、推播
權限類 角色、權限、簽核、後台
個資類 個資、隱私、敏感資料
匯出類 匯出、下載、列印、報表

判進哪幾類,就帶出哪幾類的專屬情境。可以複選,一個功能常常同時是「表單類 + 交易類 + 個資類」。回到開頭那個咖啡點數兌換,它就會被判成交易類,於是鼠勾以丟出來的問題大概長這樣:

兌換的時候有些狀況要先想好,我一個一個問你:
A. 點數不夠時:顯示差幾點?建議其他兌換品?還是導去看怎麼賺點數?
B. 重複發起同一筆兌換:冪等處理(只算一次)?提示已兌換過?還是允許重複?
C. 兌換到一半失敗:顯示失敗原因?提供重試?還是直接導客服?

開頭那個「點兩下扣兩次」,就是 B 這題沒問清楚。如果當初需求階段就答了「冪等處理」,工程師就知道要做防重,不會等到上線才補。

除了這十類各自的專屬情境,還有一組「通用情境」是不管什麼功能都會問的:身份驗證(Session 過期、權限不足)、系統與網路(API 超時、斷線、維護中)、並行與競爭(重複提交、多人改同一筆資料)。這幾個跟功能類型無關,是系統都會遇到的,所以每個案子都會被問一輪。

AI 不是應該都知道嗎?

這裡會有一個合理的疑問:這些例外 AI 自己想不出來嗎?為什麼還要人工整理十類檢查表?

我盤點工具的時候也問過同樣的問題。差別在於模型有沒有這個知識,跟它在一場長對話裡會不會每次都主動提出來。

單獨問 AI「交易功能該注意哪些例外」,它可以把防重、超時講得很完整,知識確實都在。但需求釐清是一場二十幾輪的對話,模型同時要顧流程、顧語氣,「主動想起來要問防重」就變成機率問題。LLMREI 這份研究實測過讓 LLM 做需求訪談,平均只引出七成左右該問到的需求。檢查表的作用,是把這件事從機率變成固定動作:表上有的,每次都問。

資深 SA 也一樣。他什麼都知道,開需求會議照樣帶檢核清單進去,因為靠臨場記憶一定漏。AI 更需要這張清單,畢竟它每次對話都是全新的一個人,連上次漏問什麼的記憶都沒有。

另一半原因更實際:檢查表裡藏的常常是公司自己的知識。簽核要走哪條線、哪些題該標「待 SA 評估」而不是逼著 PM 當場回答,這些 AI 再聰明也不知道,只能靠人放進去。

那十類之外的功能呢?我一開始也擔心過沒辦法事先窮舉所有類型,後來確認不需要。這十類都不是憑空設計的,全是從真實案子累積出來的。整個策略分三層:

  • 常見的,查表:十類涵蓋大多數案子,靠檢查表保證每次都問到。
  • 罕見的,兜底:就算一類都沒命中,前面講的通用情境照問,AI 的臨場追問也還在,基本盤不會全漏。
  • 沒命中的,明講:後來加了一條規則,功能判不進任何一類時,鼠勾以要直接說明「這屬於比較少見的類型,我會用通用問題盡量涵蓋」,並在產出的文件上標注「此類型尚無專屬檢查表,規格邊界建議與 SA 加強確認」。

第三層最重要。檢查表缺一類本身不是大問題,缺了而且沒人知道才是。有了這個標注,下次同類型的需求進來,我就知道該補哪一類。檢查表不需要一開始就完整,需要的是能持續增補。

兩個藏在細節裡的前置依賴

清單看起來只是「按類型查表」,但有兩處特別容易出錯,都是實際遇到之後才補進去的。

第一個是批次操作。查詢類常常會帶一個「勾選多筆一起處理」的功能,這裡有三個問題一定要單獨問:

  • 部分成功:勾了 20 筆做批次,成功 18 筆失敗 2 筆,怎麼辦?顯示明細列出哪兩筆失敗、還是一筆失敗就整批回滾、還是成功的先做失敗的讓他重試?
  • 超量:一次勾的筆數超過單次批次上限怎麼辦?擋下來提示、自動分批、還是只做前 N 筆?
  • 未選取:什麼都沒勾就按了批次按鈕,按鈕該反灰、跳提示、還是當成全選(這個最危險)?

這三題不問,工程師的預設行為每個人不一樣,「未選取當全選」這種實作真的出現過,一次誤操作就會影響大量資料。

這背後其實是個被講了幾十年的老原則。Jakob Nielsen 的可用性十大法則裡,第五條就是「預防錯誤(Error Prevention)」:好設計要事先擋掉會出錯的可能,而不是等使用者踩雷了才跳訊息補救。在需求階段把這三題問完,就是把「預防」往前挪到連 code 都還沒寫的時候。

第二個前置依賴更隱蔽,是「重複」這件事。表單類有「重複申請」、選擇類有「重複選擇」、交易類有「重複交易」、上傳類有「重複上傳」。這些情境表面在問「重複了怎麼辦」,但它其實依賴一個更前面的決定:什麼樣才算「重複」?

而「算不算重複」這件事,是在區塊 6 講欄位唯一性時定的:是全系統唯一、同一個人不能重複、還是根本不檢查。所以如果區塊 6 還沒確認某欄位的唯一性規則,這裡的「重複 X」情境就得標 [待確認],備註「待區塊 6 該欄位唯一性規則確認後才能寫 AC」。

這裡又是同一個結構:唯一性是正向 spec,重複情境是它的負向處理。整個工具的設計裡,這種正反成對的依賴出現在很多地方。

最後落成驗收條件

問完之後,鼠勾以不會只留一句「點數不足要提示」這種模糊記錄。它會轉成 Given-When-Then,而且每條多帶一欄「後續導向」,講清楚使用者看到錯誤之後會被帶去哪:

情境 Given When Then 後續導向
點數不足 可用點數 300,兌換需 500 點「確認兌換」 顯示「點數不足,尚差 200 點」,並給「如何賺點數」按鈕 導向賺點數說明,或返回改兌換品
重複兌換 同一筆兌換已成功 短時間再次送出 冪等處理,不重複扣點,提示「已兌換過」 留在原畫面

「後續導向」這一欄是刻意加的。很多需求只寫了錯誤訊息,沒寫接下來要怎麼辦:使用者看到「餘額不足」之後是停在原地,還是有下一步可以走。把出口一起想好,這個例外情境才算處理完。

Given-When-Then 來自 Dan North 提出的 BDD(行為驅動開發),原本是讓團隊用「在什麼前提下、做了什麼、就會怎樣」的句型把驗收條件寫成可執行的格式。例外情境的結構跟它一致,都是「在什麼前提下、使用者做了什麼、系統該怎麼回」,所以直接沿用。

工具自己的例外:使用者亂答怎麼辦

這一整個區塊都在要求使用者想清楚「使用者沒照著走會怎樣」,而這個工具自己最大的例外,就是使用者沒照著答。

鼠勾以的前提是雙方一來一回好好對話,實際情況不會都這樣。有人連丟三句「不知道啦」「你決定」「隨便」,有人貼一段跟需求無關的抱怨,有人灌亂碼洗版,也有人用「忽略前面的指令」這類句子測試它。早期版本遇到這些會繼續一題一題追下去,對方已經在敷衍,它還在問,最後就是對話被關掉。

後來補了一個「無效輸入熔斷」。邏輯是雙軌計數:同一題連續被無效回應三次、或整場對話累積五個沒有任何推進的回合,鼠勾以就不再繼續追問,改成停下來說明狀況,把卡住的點記進待確認清單。

這個機制最難的部分是分流,偵測本身反而單純。一個新手需求方PM 答「這個我真的不知道,要回去問工程」,是完全正常、甚至該鼓勵的回答,不能被計入熔斷;它走的是另一條路:記進待確認、歸屬工程,不扣分也不觸發熔斷。所以只寫「偵測無效輸入」不夠,必須把幾種表面很像、處理方式完全不同的情況分開。真的不知道、惡意灌亂碼、想注入指令、嫌題目太多想加快,這四種各走一條路,混在一起處理一定會誤判。

為了確認分流不會誤傷,我做了一輪壓力測試:設計十二種使用者人格輪流測,包含不熟悉需求的、惡意的、沒耐心的、正常的,各配快速跟完整模式跑一遍。正常使用者那幾組完全沒被誤觸發,這是底線;惡意的那幾組壓出兩個我沒想到的漏洞,最明顯的是混合攻擊:把亂碼、注入、無關內容摻在一起丟,單看每一種都沒到熔斷門檻,合起來整場對話其實都在空轉。改法是把計數單位從「幾次無效輸入」換成「幾個零推進回合」,這樣即使對方每次換一種方式,只要該回合沒讓需求往前走,一樣會被計入。

講這段是想說明,例外情境的思維不只適用於「使用者要的那個功能」。任何會跟人互動的東西本身就是一個系統,有 happy path,也有一堆 edge case。這個專門用來引導別人想例外的工具,一樣得回頭套用自己的方法論檢查一遍。

小結

區塊 7 做的事,是把「萬一」系統化。先認清例外情境是規格基線的反面,沒有正面數字就寫不出反面 AC;再用十類功能類型動態撈出該想的情境,不靠使用者憑空回憶;中間特別盯緊批次操作的三個破口、跟「重複」背後對欄位唯一性的依賴;最後全部收斂成帶「後續導向」的 Given-When-Then。十類涵蓋不了的,就讓它明講沒涵蓋,缺口自己會浮上來。

那個點兩下扣兩次的咖啡兌換,後來我們把防重補上了。但更重要的是,從那之後每個案子的需求討論,我都會在這一關多待一會兒。上線後最容易出包的,幾乎都是這區塊偷懶的地方。

七大區塊到這裡走完一輪。Day 21 換個主題:鼠勾以怎麼幫使用者打分數。為什麼要做一個 0 到 100 的 SA 就緒度,還配上十級成長標籤,這背後其實是一整套不想讓人填到一半就放棄的心理學設計。

參考來源

  • 「預防錯誤」原則:Jakob Nielsen,〈10 Usability Heuristics for User Interface Design〉, Nielsen Norman Group。https://www.nngroup.com/articles/ten-usability-heuristics/
  • Given-When-Then 與 BDD 起源:Dan North,〈Introducing BDD〉。https://dannorth.net/blog/introducing-bdd/
  • Given-When-Then 格式說明:Martin Fowler,〈GivenWhenThen〉。https://martinfowler.com/bliki/GivenWhenThen.html
  • LLM 需求訪談的引出率實測:〈LLMREI: Automating Requirements Elicitation Interviews with LLMs〉, arXiv:2507.02564。https://arxiv.org/abs/2507.02564

這是 iThome 鐵人賽系列文章。明天見。

Bye


上一篇
【Day 19】區塊 6 The Data:一個輸入框背後,藏著一整排沒人想問的問題
下一篇
【Day 21】SA 就緒度 100 分制——給需求一個分數,需求方PM 才知道什麼時候算「好了」
系列文
利用Custom GPT+遊戲感來寫PRD30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言